import json
import os
import shutil
import time
import zipfile
from datetime import datetime
from decimal import *

from flask import (
    Blueprint,
    render_template,
    request,
    jsonify,
    session,
    make_response,
)
from flask_login import current_user
from sqlalchemy import desc

from applications.common import curd
from applications.common.curd import model_to_dicts
from applications.common.helper import Excel
from applications.common.helper import ModelFilter
from applications.common.helper import Tools
from applications.common.helper import calculated
from applications.common.utils.http import table_api, fail_api, success_api
from applications.common.utils.rights import authorize
from applications.common.utils.validate import str_escape
from applications.extensions import db
from applications.models import Record
from applications.models import Hospital
from applications.schemas import RecordOutSchema

admin_record = Blueprint("admin_record", __name__, url_prefix="/admin/record")


# 读取pcr文件
def read_pcr(filename=""):
    if filename is None:
        return {}
    data = []
    try:
        file = open(filename, "r", encoding="utf-8")  # 打开文件
        file_data = file.readlines()  # 读取所有行
        for row in file_data:
            # tmp_list = row.replace('\t', '|')  # 去掉换行符
            row = row.replace("\n", "")  # 去掉换行符
            tmp_list = row.split("\t")
            # 去除空值
            tmp_list = [i for i in tmp_list if i != ""]
            if len(tmp_list) != 6:
                continue
            if tmp_list:
                data.append(tmp_list)  # 将每行数据插入data中
    except Exception as e:
        return fail_api(msg="上传的文件错误")

    return data


#
@admin_record.route("/")
@authorize("admin:record:main")
def main():
    role = session.get("role", [])[0]
    role_id = role[0]
    hospital_list = Tools().get_hospital_list()
    device_list = Tools().get_device_list()
    return render_template(
        "admin/record/main.html",
        role_id=role_id,
        hospital_list=hospital_list,
        device_list=device_list,
    )


#   用户分页查询
@admin_record.get("/data")
@authorize("admin:record:main")
def data():
    # 获取请求参数
    hospital_no = str_escape(request.args.get("hospital_no"))
    device_no = str_escape(request.args.get("device_no"))
    # 查询参数构造
    mf = ModelFilter()
    if hospital_no:
        mf.exact(field_name="hospital_no", value=hospital_no)
    if device_no:
        mf.exact(field_name="device_no", value=device_no)
    # 如果是医院角色只能看到自己的数据
    role = session.get("role", [])[0]
    role_id = role[0]
    if role_id == 3:
        mf.exact(field_name="hospital_no", value=session.get("hospital_no"))
    # orm查询
    # 使用分页获取data需要.items
    record = (
        Record.query.filter(mf.get_filter(Record))
        .order_by(desc(Record.id))
        .group_by(Record.random)
        .layui_paginate()
    )
    count = record.total
    # 返回api
    return table_api(
        data=model_to_dicts(schema=RecordOutSchema, data=record.items), count=count
    )


# 用户增加
@admin_record.get("/add")
@authorize("admin:record:add", log=True)
def add():
    hospital_list = Tools().get_hospital_list()
    device_list = Tools().get_device_list()
    return render_template(
        "admin/record/add.html", hospital_list=hospital_list, device_list=device_list
    )


@admin_record.post("/save")
@authorize("admin:record:add", log=True)
def save():
    req_json = request.json
    u6 = str_escape("." + req_json.get("u6"))
    u6_data = read_pcr(u6)
    if not u6_data:
        return fail_api(msg="u6文件内容错误")
    mir = str_escape("." + req_json.get("mir"))
    mir_data = read_pcr(mir)
    if not mir_data:
        return fail_api(msg="mir文件内容错误")
    device_no = str_escape(req_json.get("device_no"))
    hospital_no = str_escape(req_json.get("hospital_no"))
    user_id = current_user.id
    pcr = {}
    for row in u6_data:
        lec = row[3].split("-")
        # 格式必须为 "样本名-1","样本名-2"
        if len(lec) != 2 or lec[1].isdecimal() is not True:
            continue
        # 由于把数据插入列表时，插入的位置不能大于列表长度，所以先生成一个空的列表 再把对应的数据放到列表中
        if lec[0] not in pcr:
            x = ["" for i in range(50)]
            x.insert(int(lec[1]), {"pos": row[2], "ct": row[4]})
            pcr[lec[0]] = {"u6": x}
            del x
        else:
            x = pcr[lec[0]]["u6"]
            x.insert(int(lec[1]), {"pos": row[2], "ct": row[4]})
            pcr[lec[0]] = {"u6": x}
            del x
        del lec
    for row in mir_data:
        lec = row[3].split("-")
        if len(lec) != 2:
            continue
        if lec[0] not in pcr:
            continue
        if "mir" not in pcr[lec[0]]:
            xs = ["" for i in range(50)]
            xs.insert(int(lec[1]), {"pos": row[2], "ct": row[4]})
            pcr[lec[0]].update({"mir": xs})
            del xs
        else:
            xs = pcr[lec[0]]["mir"]
            xs.insert(int(lec[1]), {"pos": row[2], "ct": row[4]})
            pcr[lec[0]].update({"mir": xs})
            del xs
        del lec
    # user_id = current_user.id
    random = Tools.random_string_generator(8)
    # print(json.dumps(mir_data,ensure_ascii=False, indent=4)  )
    # print(json.dumps(pcr))
    # os._exit(0)
    if pcr:
        for keys in pcr:
            states = []
            exception = []
            # 判断数据格式
            if "mir" not in pcr[keys] or isinstance(pcr[keys]["mir"], list) is not True:
                states.append(2)
                exception.append("mir数据为空")
                pcr[keys]["mir"] = {}
            if "u6" not in pcr[keys] or isinstance(pcr[keys]["u6"], list) is not True:
                states.append(1)
                exception.append("u6数据为空")
                pcr[keys]["u6"] = {}
            if len(states) == 0:
                pcr[keys]["u6"] = [i for i in pcr[keys]["u6"] if i != ""]
                pcr[keys]["mir"] = [i for i in pcr[keys]["mir"] if i != ""]
                if len(pcr[keys]["u6"]) != 4:
                    states.append(1)
                    exception.append("u6数据异常")
                if len(pcr[keys]["mir"]) != 4:
                    states.append(2)
                    exception.append("mir数据异常")
            elif 1 in states:
                pcr[keys]["mir"] = [i for i in pcr[keys]["mir"] if i != ""]
                if len(pcr[keys]["mir"]) != 4:
                    states.append(2)
                    exception.append("mir数据异常")
            elif 2 in states:
                pcr[keys]["u6"] = [i for i in pcr[keys]["u6"] if i != ""]
                if len(pcr[keys]["u6"]) != 4:
                    states.append(1)
                    exception.append("u6数据异常")

            record = Record(
                hospital_no=hospital_no,
                device_no=device_no,
                user_id=user_id,
                cts=json.dumps(pcr[keys]),
                random=random,
                type=1,
                name=keys,
                states=",".join("%s" % id for id in states),
                exception=",".join("%s" % id for id in exception),
            )
            db.session.add(record)
        db.session.commit()

    return success_api(msg="增加成功")


# 删除
@admin_record.delete("/remove/<string:random>")
@authorize("admin:record:remove", log=True)
def delete(random):
    res = Record.query.filter_by(random=random).delete()
    if not res:
        db.session.rollback()
        return fail_api(msg="删除失败")
    db.session.commit()
    return success_api(msg="删除成功")


#  样本核查
@admin_record.get("/edit/<string:random>")
@authorize("admin:record:edit", log=True)
def edit(random):
    record = Record.query.filter_by(random=random).all()
    return render_template(
        "admin/record/edit.html",
        record_list=model_to_dicts(schema=RecordOutSchema, data=record),
        random=random,
    )


#  样本查看
@admin_record.get("/info/<string:random>")
@authorize("admin:record:edit", log=True)
def info(random):
    record = Record.query.filter_by(random=random).all()
    return render_template(
        "admin/record/info.html",
        record_list=model_to_dicts(schema=RecordOutSchema, data=record),
    )


#  更新
@admin_record.post("/update")
@authorize("admin:record:edit", log=True)
def update():
    random = request.form.get("random")
    ids = request.form.getlist("ids[]")
    idno = request.form.getlist("idno[]")
    username = request.form.getlist("username[]")
    sex = request.form.getlist("sex[]")
    case_no = request.form.getlist("case_no[]")
    test_time = request.form.getlist("test_time[]")
    # 删掉没勾选的记录
    Record.query.filter(Record.id.notin_(ids)).filter_by(random=random).delete()
    for i in range(len(ids)):
        record = Record.query.filter_by(id=ids[i]).first()
        record = curd.model_to_dicts(schema=RecordOutSchema, data=[record])[0]
        dx = calculated(record["cts"])["dx"]
        dx = "%.13f" % dx
        up_data = {
            "username": str_escape(username[i]),
            "case_no": str_escape(case_no[i]),
            "sex": sex[i],
            "id_card": str_escape(idno[i]),
            "test_time": str_escape(test_time[i]),
            "results": dx,
        }
        d = Record.query.filter_by(id=ids[i]).update(up_data)
        if not d:
            db.session.rollback()
            return fail_api(msg="更新失败")
    db.session.commit()
    return success_api(msg="更新成功")


def unpack(input_file, out_file):
    if os.path.exists(input_file):
        outZip = zipfile.ZipFile(input_file, "r")
        for f in outZip.namelist():
            # 解压到当前目录下的out_doc文件夹下
            outZip.extract(f, out_file)
        outZip.close()
    else:
        raise FileExistsError


def alter_xml(document_xml, data):
    with open(document_xml, "r", encoding="utf-8") as fp:
        content = fp.read()
    with open(document_xml, "w", encoding="utf-8") as fp:
        for key in data:
            curr = key
            content = content.replace(curr, str(data[key]))
        fp.write(content)


def pack(input, fileName):
    current_file_dir = os.path.dirname(fileName)
    # 如果目录不存在 先建立目录
    if not os.path.isdir(current_file_dir):
        os.makedirs(current_file_dir)
    out_zip = zipfile.ZipFile(fileName, "w", zipfile.ZIP_DEFLATED)
    for path, dirNames, fileNames in os.walk(input):
        fpath = path.replace(input, "")
        for f in fileNames:
            out_zip.write(os.path.join(path, f), os.path.join(fpath, f))
    out_zip.close()


# 模板替换
def render(doc_file, data, out_file_name):
    out_path = "doc_file_out"
    document_xml = out_path + "/word/document.xml"
    unpack(doc_file, out_path)
    alter_xml(document_xml, data)
    pack(out_path, out_file_name)
    shutil.rmtree(out_path, ignore_errors=True)


#  根据身份证号获取年龄
def get_age(number):
    current = int(time.strftime("%Y"))
    year = int(number[6:10])
    return format(current - year)


#  导出记录
@admin_record.get("/r_download/<string:random>")
@authorize("admin:record:r_download", log=True)
def r_download(random):
    r = [""]
    r[0] = random
    record = Excel().record_out_excel(r)
    resp = make_response(record.getvalue())
    rInfo = Record.query.filter_by(random=random).first()
    if rInfo != None:
        h = Hospital.query.filter_by(hospital_no=rInfo.hospital_no).first()
        filename = "预测记录-" + h.name + "（" + datetime.now().strftime("%Y%m%d") + "）.xlsx"
    else:
        filename = "预测记录（" + datetime.now().strftime("%Y%m%d") + "）.xlsx"
    latinFileName = filename.encode("utf-8").decode("latin1")  # 编码转换
    resp.headers["Content-Disposition"] = "attachment; filename=" + latinFileName
    resp.headers["Content-Type"] = "application/x-xlsx"
    return resp


@admin_record.get("/r_downloads/<string:randoms>")
@authorize("admin:record:r_download", log=True)
def r_downloads(randoms):
    record = Excel().record_out_excel(randoms.split(","))
    resp = make_response(record.getvalue())
    filename = "预测记录（" + datetime.now().strftime("%Y%m%d") + "）.xlsx"
    latinFileName = filename.encode("utf-8").decode("latin1")  # 编码转换
    resp.headers["Content-Disposition"] = "attachment; filename=" + latinFileName
    resp.headers["Content-Type"] = "application/x-xlsx"
    return resp


# 下载报告
@admin_record.post("/t_download")
@authorize("admin:record:t_download", log=True)
def t_download():
    json = request.json
    info_id = (json.get("id"),)
    record = curd.get_one_by_id(Record, info_id)
    record = curd.model_to_dicts(schema=RecordOutSchema, data=[record])[0]
    x = calculated(record["cts"])["x"]
    rdata = {
        "{": "",
        "}": "",
        "username": record["username"],
        "age": get_age(record["id_card"]),
        "idcard": record["id_card"],
        "caseno": record["case_no"],
        "testtime": record["test_time"],
        "ct1": x[0],
        "ct2": x[1],
        "ct3": x[2],
        "ct4": x[3],
        "sex": "男士" if record["sex"] == 1 else "女士",
        "jieguo": record["results"],
        "results": "阳性" if record["results"] > Decimal("0.62") else "阴性",
    }
    file_name = "《" + record["username"] + "-（" + record["case_no"] + "）预测报告》.docx"
    out_file = (
        "./static/template/" + datetime.now().strftime("%Y%m%d") + "/" + file_name
    )
    render("./static/template/template.docx", rdata, out_file)

    # 更新下载次数
    up_data = {"dow_count": record["dow_count"] + 1}
    d = Record.query.filter_by(id=info_id).update(up_data)
    if not d:
        db.session.rollback()
        return fail_api(msg="更新失败")
    db.session.commit()

    res = {
        "msg": "生成成功",
        "code": 0,
        "success": True,
        "data": {
            "src": "/static/template/"
            + datetime.now().strftime("%Y%m%d")
            + "/"
            + file_name
        },
    }
    return jsonify(res)


# 根据身份证查询记录
@admin_record.post("/searchId")
@authorize("admin:record:main", log=True)
def search_id():
    id_card = str_escape(request.json.get("id_card"))
    record = Record.query.filter_by(id_card=id_card).first()
    res = {
        "msg": "查询成功",
        "code": 0,
        "success": True,
        "data": curd.model_to_dicts(schema=RecordOutSchema, data=[record])[0],
    }
    return jsonify(res)


# 批量删除
@admin_record.delete("/batchRemove")
@authorize("admin:record:remove", log=True)
def batch_remove():
    randoms = request.form.getlist("randoms[]")
    for random in randoms:
        res = Record.query.filter_by(random=random).delete()
        if not res:
            return fail_api(msg="批量删除失败")
    db.session.commit()
    return success_api(msg="批量删除成功")
